@
@ asm.S : helper functions in assembly
@

#include <bits/swi.h>

.extern kernel_sp
.extern global_data

@This function switches to user mode and transfers control to user program
.global switch_user_mode
switch_user_mode:
	stmfd sp!, {r0-r12, lr}     @Save registers on stack
	ldr r1, =kernel_sp          @Save value of SP 
	str sp, [r1]
	mrs r0, cpsr                @Get the value of cpsr
	bic r0, r0, #0xf            @Change to user mode
    bic r0, r0, #128
	mov r1, sp
	msr cpsr, r0
	mov sp, r1                  @Make user mode SP point to kernel stack
	ldmfd sp!, {r0-r12, lr}     @pop saved registers
	mov sp, r0                  @Load the correct stack pointer in SP
	mov r0, #0xa                @Jump to 0xa0000000
	mov lr, pc
	mov pc, r0, ror #4      
	swi EXIT_SWI                @User program returns here, so exit.

.global exit
exit:
	ldr sp, =kernel_sp          @Get the saved kernel SP
	ldr sp, [sp]
    str r0, [sp]                @Save status on stack
	ldmfd sp!, {r0-r12, lr}     @pop the saved registers
	mov pc, lr                  @jump back to kernel main

.extern irq_handler
.extern irq_stack
.global irq_wrapper
irq_wrapper:
    ldr sp, =irq_stack		@Make sp point to irq_stack
    str sp, [sp]
    @we are using multiple add instructions instead of a single ldr followed 
    @by add because we observed that this approach was faster in terms of 
    @execution speed
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    add sp, sp, #128
    stmfd sp!, {r0-r12, lr}
    bl irq_handler			    @branch to irq_handler
    ldmfd sp!, {r0-r12, lr}
    subs pc, lr, #4			    @return to user program

.global enable_irqs
enable_irqs:
    stmfd sp!, {r0-r12, lr}
    mrs r0, cpsr			@Copy cpsr in register r0
    bic r0, r0, #128		@set I bit in cpsr to 0
    msr cpsr, r0			@Restore cpsr
    ldmfd sp!, {r0-r12, lr}
    mov pc, lr

.global disable_irqs
disable_irqs:
    stmfd sp!, {r0-r12, lr}
    mrs r0, cpsr			@Copy cpsr in register r0
    orr r0, r0, #128		@Set I bit in cpsr to 1
    msr cpsr, r0			@Restore cpsr
    ldmfd sp!, {r0-r12, lr}
    mov pc, lr

